#ifndef __IM_PROPERTIES_H__
#define __IM_PROPERTIES_H__

#include <im_base_types.h>
#include <im_object.h>
#include <im_string.h>
#include <im_text.h>

/**
 * @defgroup IMProperty
 * @ingroup PublicIME
 * @{
 */

/**
 * @struct IMProperty
 * @brief An object holds a property of an IMBUS component.
 *
 * A property of an IMBUS component represents a state or
 * a switch of the component, such as current input mode or
 * something like that.
 *
 * GUI component can accept properties registered by other IMBUS
 * components and draw them on screen in arbitrary form
 * (a button, a label, etc.)
 *
 * User can trigger a property by specified method, such as by
 * clicking on it. An event will be sent back to the component
 * which owns the property as soon as it's triggered by user.
 * So that the component can adjust its internal state accordingly.
 *
 * There are four @copydoc IMPropertyType
 *
 * A property contains the following elements:
 * - Key
 *   type: IMString*
 *   An unique string key to identify the property.
 *   A separator property should have an unique string key as well,
 *   so that it can be manipulated by IMPropList object.
 * - Label
 *   type: IMText*
 *   A human readable text label which will be displayed on
 *   the screen.
 *   It doesn't apply to separator property.
 * - Icon
 *   type: IMString*
 *   An icon file which will be displayed on the screen along
 *   with label. A URI shall be used here, and file URI shall be supported by
 *   default. Other kind of URIs are optional.
 *   It doesn't apply to separator property.
 * - Tip
 *   type: IMText*
 *   A human readable text to describe the meaning of the property,
 *   which may be displayed on the screen.
 *   It doesn't apply to separator property.
 * - Sub Properties
 *   type: IMPropList *
 *   Optionally a IMPropList object contains one or more sub-properties.
 *   With sub-properties list, we can organize multiple properties
 *   in hierarchy style.
 *   It doesn't apply to separator property.
 *
 * Multiple properties may be organized in hierarchy style
 * by attaching a IMPropList object, which contains sub-properties,
 * to a property. A property with sub-properties attached becomes
 * root property.
 *
 * A reasonable screen display form of a multi-level properties
 * hierarchy can look like a menu:
 *
 * @code
 * +-------+
 * | prop1 |
 * | prop2-+->+-------+
 * | prop3 |  | prop5 |
 * |-------|  | prop6-+->+-------+
 * | prop4 |  | prop7 |  | prop8 |
 * +-------+  +-------+  | prop9 |
 *                       +-------+
 * @endcode
 *
 * In such case, prop2 is root property of prop5,prop6 and prop7.
 * While prop6 is root property of prop8 and prop9.
 */

/**
 * @struct IMPropList
 * @brief An object to hold multiple properties.
 *
 * When an IMBUS component wants to send some properties to
 * GUI component, it must first put these properties into
 * a IMPropList object in sequence, then send it to GUI
 * component.
 */

/**
 * @struct IMPropIterator
 * @brief Iterator of IMPropList to iterate all properties
 *        stored in a IMPropList object.
 */

IM_BEGIN_DECLS

/** @brief cast the pointer to IMProperty*, return NULL if failed. */
#define IM_PROPERTY(p)              (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_PROPERTY,IMProperty))
/** @brief cast the pointer to const IMProperty*, return NULL if failed. */
#define IM_CONST_PROPERTY(p)        (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_PROPERTY,IMProperty))
/** @brief cast the pointer to IMPropertyClass*, return NULL if failed. */
#define IM_PROPERTY_CLASS(c)        (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_PROPERTY,IMPropertyClass))
/** @brief tell if the pointer is an instance of IMProperty */
#define IM_IS_PROPERTY(p)           (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_PROPERTY))
/** @brief tell if the pointer is an IMPropertyClass */
#define IM_IS_PROPERTY_CLASS(c)     (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_PROPERTY))
/** @brief get the IMPropertyClass from an instance of IMProperty */
#define IM_PROPERTY_GET_CLASS(p)    (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_PROPERTY,IMPropertyClass))

/** @brief cast the pointer to IMPropList*, return NULL if failed. */
#define IM_PROP_LIST(p)             (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_PROP_LIST,IMPropList))
/** @brief cast the pointer to const IMPropList*, return NULL if failed. */
#define IM_CONST_PROP_LIST(p)       (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_PROP_LIST,IMPropList))
/** @brief cast the pointer to IMPropListClass*, return NULL if failed. */
#define IM_PROP_LIST_CLASS(c)       (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_PROP_LIST,IMPropListClass))
/** @brief tell if the pointer is an instance of IMPropList */
#define IM_IS_PROP_LIST(p)          (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_PROP_LIST))
/** @brief tell if the pointer is an IMPropListClass */
#define IM_IS_PROP_LIST_CLASS(c)    (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_PROP_LIST))
/** @brief get the IMPropListClass from an instance of IMPropList */
#define IM_PROP_LIST_GET_CLASS(p)   (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_PROP_LIST,IMPropListClass))

typedef struct _IMProperty          IMProperty;
typedef struct _IMPropertyClass     IMPropertyClass;

typedef struct _IMPropList          IMPropList;
typedef struct _IMPropListClass     IMPropListClass;

typedef struct _IMPropIterator      IMPropIterator;

/**
 * @enum IMPropertyType
 * @brief types of an IMProperty.
 *
 * - IM_PROPERTY_NORMAL
 *   Normal property that represents a state of IMBUS component.
 *   It can be triggerred by user.
 *   Normal property may be displayed as a normal button or menu
 *   item by GUI component.
 * - IM_PROPERTY_TOGGLE
 *   Like normal property, a toggle property can represent a state
 *   of IMBUS component and can be triggerred by user.
 *   But GUI component may display a toggle property with different
 *   appearance which indicates that it can be turned on or off
 *   (checked or unchecked).
 * - IM_PROPERTY_RADIO
 *   Similar than toggle property, but it may have a different
 *   appearance which indicates that it's one of an items group,
 *   in which only one item can be selected at one time. Just
 *   like radio buttons widget used in GUI interface.
 * - IM_PROPERTY_SEPARATOR
 *   A separator property doesn't carry any state information of IMBUS
 *   component. It will just be displayed as a separator to split
 *   other properties into different groups.
 *   Thus it can't be triggerred by user.
 */
typedef enum
{
    IM_PROPERTY_NORMAL = 0,
    IM_PROPERTY_TOGGLE,
    IM_PROPERTY_RADIO,
    IM_PROPERTY_SEPARATOR
} IMPropertyType;

/**
 * @enum IMPropertyCheckState
 * @brief Value to Indicate the different states of a checkable property..
 *
 * Only radio and toggle property are checkable.
 *
 * - IM_PROPERTY_UNCHECKED
 *   Indicates that this property is currently not checked.
 * - IM_PROPERTY_INCONSISTENT
 *   Indicates that this property is currently in inconsitent state, neither
 *   checked nor unchecked. It's a state beteen unchecked and checked.
 * - IM_PROPERTY_CHECKED
 *   Indicates that this property is currently checked.
 */
typedef enum
{
    IM_PROPERTY_UNCHECKED = 0,
    IM_PROPERTY_INCONSISTENT = 1,
    IM_PROPERTY_CHECKED = 2,
} IMPropertyCheckState;

/**
 * @brief Create a new property with a specified key string.
 *
 * @param key   the string id of this property, does not check the key duplication.
 *
 * @return      the newly created IMProperty object.
 *
 * The key IMString object will only be referenced rather than cloned.
 *
 * The default type is IM_PROPERTY_NORMAL. Sensitive and visible flags will be
 * set to TRUE by default. Check state will be set to IM_PROPERTY_UNCHECKED by
 * default.
 */
IMProperty*         im_property_new                 (IMString           *key);

/**
 * @brief Create a new property with specified parameters.
 *
 * @param type      the IMPropertyType
 * @param key       the string id of this property
 * @param icon      the icon id of this property
 * @param label     the label (IMText*) of this property
 * @param tip       the tip (IMText*) of this property
 *
 * The objects (not primitive types) in parameter list will only be referenced
 * rather than cloned.
 *
 * @return          the newly created IMProperty object.
 */
IMProperty*         im_property_new_full            (IMPropertyType      type,
                                                     IMString           *key,
                                                     IMString           *icon,
                                                     IMText             *label,
                                                     IMText             *tip,
                                                     IMBool              sensitive,
                                                     IMBool              visible,
                                                     IMPropertyCheckState check_state);
/**
 * @brief Set the type of specified property.
 *
 * @param prop  an IMProperty
 * @param type  an IMPropertyType
 *
 * If the type is set to IM_PROPERTY_SEPARATOR, all original information, except
 * the string key, will be lost.
 */
void                im_property_set_type            (IMProperty         *prop,
                                                     IMPropertyType      type);
/**
 * @brief Get the type of specified property.
 *
 * @param prop  an IMProperty
 *
 * @return      the property type
 */
IMPropertyType      im_property_get_type            (const IMProperty   *prop);

/**
 * @brief Set the sensitive state of a specified property.
 *
 * Only sensitive property can be triggered/selected by user.
 *
 * @param prop Pointer to an IMProperty object.
 * @param sensitive Indicates if the specified IMProperty object will be
 *                  sensitive or not.
 */
void                im_property_set_sensitive       (IMProperty         *prop,
                                                     IMBool              sensitive);
/**
 * @brief Check if a specified IMProperty object is sensitive.
 *
 * @param prop Pointer to an IMProperty object.
 * @return TRUE if the specified property is currently sensitive.
 */
IMBool              im_property_is_sensitive        (const IMProperty   *prop);

/**
 * @brief Set the visible state of a specified property.
 *
 * If a property is invisible, all properties belongs to it and itself won't
 * be displayed.

 * @param prop Pointer to an IMProperty object.
 * @param visible Indicates if the specified IMProperty object will be
 *                visible or not.
 */
void                im_property_set_visible         (IMProperty         *prop,
                                                     IMBool              visible);

/**
 * @brief Check if a specified IMProperty object is visible.
 *
 * @param prop Pointer to an IMProperty object.
 * @return TRUE if the specified property is currently visible.
 */
IMBool              im_property_is_visible          (const IMProperty   *prop);

/**
 * @brief Set the check state of a specified IMProperty object.
 *
 * @param prop Pointer to an IMProperty object.
 * @param state The check state to be set, @sa IMPropertyCheckState.
 */
void                im_property_set_check_state     (IMProperty         *prop,
                                                     IMPropertyCheckState state);
/**
 * @brief Get the check state of a specified IMProperty object.
 *
 * @param prop Pointer to an IMProperty object.
 * @return The current check state of the specified IMProperty object.
 */
IMPropertyCheckState im_property_get_check_state    (IMProperty         *prop);

/**
 * @brief Set key string of a property.
 *
 * @param prop  an IMProperty
 * @param key   the string id
 *
 * The key IMString object will only be referenced rather than cloned.
 */
void                im_property_set_key             (IMProperty         *prop,
                                                     IMString           *key);
/**
 * @brief Get key string of a specified IMProperty object.
 *
 * @param prop  an IMProperty
 *
 * @return      the string id
 */
IMString*           im_property_get_key             (const IMProperty   *prop);
/**
 * @brief Set icon file of a property.
 *
 * @param prop  an IMProperty
 * @param icon  URI to the icon, file URI shall be supported by default, others
 *              are optional.
 *
 * The icon IMString object will only be referenced rather than cloned.
 */
void                im_property_set_icon            (IMProperty         *prop,
                                                     IMString           *icon);
/**
 * @brief Get icon uri of a specified IMProperty object.
 *
 * @param prop  an IMProperty
 *
 * @return      URI to the icon.
 */
IMString*           im_property_get_icon            (const IMProperty   *prop);
/**
 * @brief Set the label of a specofied IMProperty object.
 *
 * @param prop  an IMProperty
 * @param label the label object
 *
 * The label IMText object will only be referenced rather than cloned.
 */
void                im_property_set_label           (IMProperty         *prop,
                                                     IMText             *label);
/**
 * @brief Get tje label of a specified IMProperty object.
 *
 * @param prop  an IMProperty
 *
 * @return the label object
 */
IMText*             im_property_get_label           (const IMProperty   *prop);
/**
 * @brief Set the tip of a specofied IMProperty object.
 *
 * @param prop  an IMProperty
 * @param tip   the tip object
 *
 * The tip IMText object will only be referenced rather than cloned.
 */
void                im_property_set_tip             (IMProperty         *prop,
                                                     IMText             *tip);
/**
 * @brief Get the tip of a specified IMProperty object.
 *
 * @param prop  an IMProperty
 *
 * @return the tip object
 */
IMText*             im_property_get_tip             (const IMProperty   *prop);

/**
 * @brief Attach or remove a list of sub properties
 *        to a specified property.
 *
 * @param prop      an IMProperty
 * @param subprops  an IMPropList object contains one or more sub properties,
 *                  or 0 to remove the old list.
 *
 * The subprops IMPropList object will only be referenced rather than cloned.
 */
void                im_property_set_sub_props       (IMProperty         *prop,
                                                     IMPropList         *subprops);
/**
 * @brief Get sub properties list attached to a specified property.
 *
 * @param prop  an IMProperty
 *
 * @return The IMPropList object which contains sub properties,
 *         or 0 if there is no sub properties.
 *         Caller shouldn't modify or release the returned object.
 */
IMPropList*         im_property_get_sub_props       (const IMProperty   *prop);

/**
 * @brief Create a new IMPropList object.
 *
 * @return the newly created IMPropList.
 */
IMPropList*         im_prop_list_new                ();

/**
 * @brief Clear an IMPropList object. All IMProperty objects held by
 * it will be released.
 *
 * @param list  an IMPropList
 */
void                im_prop_list_clear              (IMPropList         *list);
/**
 * @brief Check whether a specified IMPropList object is empty or not.
 *
 * @param list  an IMPropList
 * @return TRUE if list is an IMPropList, else FALSE;
 */
IMBool              im_prop_list_empty              (const IMPropList   *list);
/**
 * @brief Append an IMProperty object into a specified IMPropList object.
 *
 * @param list  an IMPropList
 * @param prop  an IMProperty
 */
void                im_prop_list_append             (IMPropList         *list,
                                                     IMProperty         *prop);
/**
 * @brief Remove a IMProperty object with specified key
 * from a specified IMPropList object.
 *
 * @param list  an IMPropList
 * @param key   the key of an IMProperty
 */
void                im_prop_list_remove             (IMPropList         *list,
                                                     const IMChar       *key);
/**
 * @brief Get a IMProperty object with specified key stored in
 * a specified IMPropList object.
 *
 * @param list  an IMPropList
 * @param key   the key of an IMProperty
 *
 * @return the IMProperty object with the specified key.
 */
IMProperty*         im_prop_list_get                (const IMPropList   *list,
                                                     const IMChar       *key);
/**
 * @brief Call a specified function against each IMProperty objects stored in
 * a specified IMPropList object.
 *
 * @param list      an IMPropList
 * @param func      the iterator callback function
 * @param user_data the user data passed to the callback function
 */
void                im_prop_list_foreach            (const IMPropList   *list,
                                                     IMFunc              func,
                                                     IMPointer           user_data);
/**
 * @brief Create an iterator for a specified IMPropList object.
 *
 * @param list  an IMPropList
 *
 * @return an iterator
 */
IMPropIterator*     im_prop_list_get_iterator       (IMPropList         *list);
/**
 * @brief Clone a IMPropIterator structure.
 *
 * @param iter  an IMPropIterator
 *
 * @return the cloned iterator
 */
IMPropIterator*     im_prop_iterator_clone          (const IMPropIterator *iter);
/**
 * @brief Destroy a IMPropIterator instance.
 *
 * @param iter  an IMPropIterator
 */
void                im_prop_iterator_destroy        (IMPropIterator     *iter);
/**
 * @brief Advance a IMPropIterator instance to point to the next
 * IMProperty object inside its associated IMPropList object.
 *
 * @param   an IMPropIterator
 *
 * @return TRUE if the current list has next element, else FALSE.
 */
IMBool              im_prop_iterator_next           (IMPropIterator     *iter);
/**
 * @brief Get the IMProperty object pointed by a specified IMPropIterator
 * instance.
 *
 * @param   an IMPropIterator
 *
 * @return  the IMProperty object associated with this iterator
 */
IMProperty*         im_prop_iterator_get            (const IMPropIterator *iter);
/**
 * @brief Check whether a IMPropIterator instance is pointed to a valid
 * IMProperty object.
 *
 * @param   an IMPropIterator
 *
 * @return  TRUE if the iterator refers to a valid IMProperty object, else FALSE.
 */
IMBool              im_prop_iterator_valid          (const IMPropIterator *iter);
/**
 * @brief Check whether two IMPropIterator instances point to the same
 * IMProperty object or not.
 *
 * @param iter1 an IMPropIterator
 * @param iter2 another IMPropIterator
 *
 * @return TRUE if iter1 and iter2 point to the same IMProperty object, else FALSE.
 */
IMBool              im_prop_iterator_equal          (const IMPropIterator *iter1,
                                                     const IMPropIterator *iter2);
/**
 * @brief Remove the IMProperty object pointed by specified IMPropIterator
 * instance from its associated IMPropList object.
 *
 * @param iter  an IMPropIterator
 */
void                im_prop_iterator_remove         (IMPropIterator     *iter);

IM_END_DECLS
/** @} */

#endif
/*
vi:ts=4:nowrap:ai:expandtab
*/
